/**
 * 200109:废弃
 * 210824：升级修改【three.js r120-r131】
 */
let THREE=require('three');
import Props from "../general/props";

/**
 *网格绘制点
 *
 @param values {object} 属性对象
 size:1,  尺寸
 color:new THREE.Color(1,1,1), 颜色（颜色与贴图颜色叠加（相乘））
 map:null, 贴图（texture）
 opacity:1, 不透明度
 sizeAttenuation:true, 近大远小
 perspectiveFactor:17.3, 透视系数（不常用，有投影模式切换时会用）
 castRatio:1, 射线检测倍率
 zOffset:0, 深度偏移

 获取material编辑材质通用属性
 获取geometry编辑通用几何属性
 */
export default class MeshPoint extends THREE.Object3D{
    constructor(values={}){
        super();
        console.warn('this class is deprecated now, use MeshPoints instead!');
        this.isMesh=true;
        this.drawMode=THREE.TrianglesDrawMode;
        this.material=new THREE.ShaderMaterial();
        this.geometry=new THREE.BufferGeometry();
        this._values=values;
        this._init();
    }

    /**
     * 获取属性
     * @returns {null|props}
     */
    get props(){
        return this._props;
    }

    /**
     * 设置属性
     * @param values {object} {name,value}
     */
    set props(values){
        for(let key in values){
            if(this._props[''+key+'']){
                this._props[''+key+''].value=values[''+key+''];
            }else{
                console.warn(''+key+' is not a prop of MeshPoint!');
            }
        }
    }

    /**
     * 获取包围球
     * @returns {Sphere}
     */
    get boundingSphere(){
        return this._boundingSphere;
    }

    /**
     * 尺寸
     */
    get size(){
        return this._props.size.value;
    }
    set size(num){
        this._props.size.value=num;
    }

    /**
     * 射线检测倍率
     */
    get castRatio(){
        return this._props.castRatio.value;
    }
    set castRatio(num){
        this._props.castRatio.value=num;
    }

    /**
     * 设置深度偏移
     */
    get zOffset(){
        return this._props.zOffset.value;
    }
    set zOffset(num){
        this._props.zOffset.value=num;
    }

    /**
     * 设置透视系数（不常用）
     */
    get perspectiveFactor(){
        return this._props.perspective.value;
    }
    set perspectiveFactor(num){
        this._props.perspectiveFactor.value=num;
    }

    /**
     * 是否启用近大远小
     */
    get sizeAttenuation(){
        return this._props.sizeAttenuation.value;
    }
    set sizeAttenuation(bol){
        this._props.sizeAttenuation.value=bol;
    }

    /**
     * 获取不透明度
     */
    get opacity(){
        return this._props.opacity.value;
    }
    set opacity(num){
        this._props.opacity.value=num;
    }

    /**
     * 获取贴图
     */
    get map(){
        return this._props.map.value;
    }
    set map(texture){
        this._props.map.value=texture;
    }

    /**
     * 颜色（颜色与贴图颜色叠加（相乘））
     */
    get color(){
        return this._props.color.value;
    }
    set color(color){
        this._props.color.value=color;
    }

    /**
     * 获取顶点着色器
     * @returns {string}
     */
    get vertexShader(){
        return [
            'attribute vec2 direction;' ,
            'uniform float size;',
            'uniform bool sizeAttenuation;',
            'uniform float perspectiveFactor;',
            'uniform float zOffset;',
            'varying vec2 vuv;',
            'float computeAspect(){' ,
            '   float aspect=0.5625;' ,
            '   if(projectionMatrix[3][3]==0.0){' ,
            '      float _tan=projectionMatrix[1][1];' ,
            '      float as_tan=1.0/projectionMatrix[0][0];' ,
            '      aspect=1.0/(as_tan*_tan);' ,
            '   }else{' ,
            '      float _height=1.0/projectionMatrix[1][1];' ,
            '      float _width=1.0/projectionMatrix[0][0];' ,
            '      aspect=_height/_width;' ,
            '   }' ,
            '   return aspect;' ,
            '}',
            'void main(){' ,
            '   vec4 mvPosition=modelViewMatrix*vec4(position,1.0);',
            '   vec4 pmvPosition=projectionMatrix*mvPosition;' ,

            '   vec3 dv=vec3(direction.xy,0.);' ,
            '   float aspect=computeAspect();' ,
            '   dv.x*=aspect;' ,
            '   dv=normalize(dv)*size;' ,
            '   if(projectionMatrix[3][3]!=0.){' ,
            '       dv*=projectionMatrix[1][1];' ,
            '   }' ,
            '   pmvPosition.z+=zOffset;' ,
            '   if(sizeAttenuation){' ,
            '      gl_Position=vec4(vec3(pmvPosition.xyz)+dv/perspectiveFactor,pmvPosition.w);' ,
            '   }else{' ,
            '      gl_Position=vec4(vec3(pmvPosition.xyz)+dv/perspectiveFactor*pmvPosition.w,pmvPosition.w);',
            '   }',

            '   vuv=uv;',
            '}'
        ].join('\n');
    }

    /**
     * 获取片元着色器
     * @returns {string}
     */
    get fragmentShader(){
        return [
            this._props.map.value?'#define MAP_AVAILABLE':'',
            'uniform float opacity;' ,
            'uniform vec3 color;',
            '#ifdef MAP_AVAILABLE',
            '   uniform sampler2D map;' ,
            '#endif',
            'varying vec2 vuv;' ,
            'void main(){' ,
            '   #ifdef MAP_AVAILABLE' ,
            '      vec4 col=texture2D(map,vuv);' ,
            '      col.r*=color.r*opacity;',
            '      col.g*=color.g*opacity;',
            '      col.b*=color.b*opacity;',
            '      gl_FragColor=col;' ,
            '   #else' ,
            '      gl_FragColor=vec4(color.rgb,opacity);' ,
            '   #endif' ,
            '}'
        ].join('\n');
    }

    /**
     * 计算包围球（size更改后需要）
     */
    computeBoundingSphere(){
        this._boundingSphere=new THREE.Sphere(new THREE.Vector3(0,0,0),this._props.size.value);
    }

    /**
     * 拷贝
     * @param source {MeshPoint}
     */
    copy(source){
        this.material.copy(source.material);
        this.props=source.props.all(true);
        this.material.needsUpdate=true;
        this.computeBoundingSphere();
    }

    /**
     * 克隆
     * @returns {MeshPoint}
     */
    clone(){
        let c=new MeshPoint();
        c.copy(this);
        return c;
    }

    /**
     * 销毁
     */
    dispose(){
        this.geometry.dispose();
        if(this.material.uniforms.map.value)this.material.uniforms.map.value.dispose();
        this.material.dispose();
        this._props.dispose();
        this._values=null;
        this._props=null;
    }

    _init(){
        this._initProps();
        this._initVariable();
        this._initComponents();
        this._initRaycast();
        this._initPropCallback();
    }
    _initProps(){
        this._props=new Props({
            size:this._values.size||1,
            color:this._values.color||new THREE.Color(1,1,1),
            map:this._values.map||null,
            opacity:this._values.opacity||1,
            sizeAttenuation:this._values.sizeAttenuation===undefined?true:this._values.sizeAttenuation,
            perspectiveFactor:this._values.perspectiveFactor||2,
            castRatio:this._values.castRatio||1,
            zOffset:this._values.zOffset||0,
        });
    }
    _initPropCallback(){
        this._props.onKeyChange('zOffset',(_new,_old)=>{
            this.material.uniforms.zOffset.value=_new;
        });
        this._props.onKeyChange('perspectiveFactor',(_new,_old)=>{
            this.material.uniforms.perspectiveFactor.value=_new;
        });
        this._props.onKeyChange('sizeAttenuation',(_new,_old)=>{
            this.material.uniforms.sizeAttenuation.value=_new;
        });
        this._props.onKeyChange('opacity',(_new,_old)=>{
            this.material.uniforms.opacity.value=_new;
        });
        this._props.onKeyChange('map',(_new,_old)=>{
            if((!_old&&_new)||(_old&&!_new))this.material.fragmentShader=this.fragmentShader;
            this.material.uniforms.map.value=_new;
            this.material.needsUpdate=true;
        });
        this._props.onKeyChange('size',(_new,_old)=>{
            this.material.uniforms.size.value=_new;
        });
        this._props.onKeyChange('color',(_new,_old)=>{
            this.material.uniforms.color.value=_new;
        });
    }
    _initVariable(){}
    _initComponents(){
        this._initGeometry();
        this._initMaterial();
    }
    _initGeometry(){
        let ps=new Float32Array([0,0,0,  0,0,0,  0,0,0,  0,0,0]);
        let us=new Float32Array([0,0,    0,1,    1,1,    1,0]);
        let ds=new Float32Array([-1,-1,  -1,1,   1,1,    1,-1]);
        this.geometry.setIndex([0,2,1,0,3,2]);
        this.geometry.setAttribute('position',new THREE.BufferAttribute(ps,3));
        this.geometry.setAttribute('uv',new THREE.BufferAttribute(us,2));
        this.geometry.setAttribute('direction',new THREE.BufferAttribute(ds,2));
        this.computeBoundingSphere();
    }
    _initMaterial(){
        this.material.vertexShader=this.vertexShader;
        this.material.fragmentShader=this.fragmentShader;
        this.material.uniforms={
            size:{value:this._props.size.value},
            opacity:{value:this._props.opacity.value},
            map:{value:this._props.map.value},
            sizeAttenuation:{value:this._props.sizeAttenuation.value},
            perspectiveFactor:{value:this._props.perspectiveFactor.value},
            color:{value:this._props.color.value},
            zOffset:{value:this._props.zOffset.value},
        };
        this.material.depthTest=true;
        this.material.depthWrite=false;
        this.material.transparent=false;
        // this.material.blending=THREE.AdditiveBlending;
    }
    _initRaycast(){
        this.raycast=(()=>{
            let inverseMatrix = new THREE.Matrix4();
            let ray = new THREE.Ray();
            let sphere = new THREE.Sphere();
            return (raycaster,intersects)=>{
                let object = this;
                let geometry = this.geometry;
                let matrixWorld = this.matrixWorld;
                let threshold = this._props.size.value*this._props.castRatio.value;
                if ( this._boundingSphere === null ) this.computeBoundingSphere();
                sphere.copy( this._boundingSphere );
                sphere.applyMatrix4( matrixWorld );
                sphere.radius += threshold;
                if ( raycaster.ray.intersectsSphere( sphere ) === false ) return;
                // inverseMatrix.getInverse( matrixWorld );20210824
                inverseMatrix.copy( matrixWorld ).invert();
                ray.copy( raycaster.ray ).applyMatrix4( inverseMatrix );
                let localThreshold = threshold / ( ( this.scale.x + this.scale.y + this.scale.z ) / 3 );
                let localThresholdSq = localThreshold * localThreshold;
                let position = new THREE.Vector3();
                let testPoint=( point, index )=> {
                    let rayPointDistanceSq = ray.distanceSqToPoint( point );
                    if ( rayPointDistanceSq < localThresholdSq ) {
                        let intersectPoint =new THREE.Vector3();
                        ray.closestPointToPoint( point ,intersectPoint);
                        intersectPoint.applyMatrix4( matrixWorld );
                        let distance = raycaster.ray.origin.distanceTo( intersectPoint );
                        if ( distance < raycaster.near || distance > raycaster.far ) return;
                        intersects.push( {
                            distance: distance,
                            distanceToRay: Math.sqrt( rayPointDistanceSq ),
                            point: intersectPoint.clone(),
                            index: index,
                            face: null,
                            object: object
                        } );
                    }
                };
                if ( geometry.isBufferGeometry ) {
                    let positions =geometry.getAttribute('position').array;
                    for (let i = 0, l = positions.length / 3; i < l; i++) {
                        position.fromArray(positions, i * 3);
                        testPoint(position, i);
                    }
                }
            };
        })();
    }
}
